
package ai.human

import java.util.List

import ai.AIEngine
import data.Player
import data.Position
import data.Data.PlayerState
import data.Data.Role
import data.Data.TeamID
import engine.Pitch

/**
 * Player group manager.
 * 
 * @author Tomasz Dąbrowski
 *
 */
class PlayerGroupsManager {

	/**
	 * AI engine.
	 */
	AIEngine aiEngine
	/**
	 * Groups.
	 */
	List<PlayerGroup> groups
	/**
	 * Positions reqs.
	 */
	PositionReqs positionReqs
	/**
	 * Leader group.
	 */
	PlayerGroup leaderGroup

	/**
	 * Team id.
	 */
	def teamID = { aiEngine.team.id }

	/**
	 * Generates positions reqs.
	 *
	 *
	 * @author Tomasz Dąbrowski
	 */
	public void generatePositionReqs() {
		positionReqs = new PositionReqs(positions : aiEngine.pitchTeam(aiEngine.team.id))
	}

	/**
	 * Sets groups.
	 *
	 * @param groups groups
	 *
	 * @author Tomasz Dąbrowski
	 */
	private void setGroups(List<PlayerGroup> groups) {
		this.groups = groups.sort { PlayerGroup pg ->
			pg.mainPlayer.role().value()
		}
	}

	/**
	 * Sets player.
	 *
	 * @param players players
	 *
	 * @author Tomasz Dąbrowski
	 */
	public void setPlayers(List<Player> players) {
		List<PlayerGroup> pgs = PlayerGroupsManager.createGroups(players)
		setGroups(pgs)
	}


	/**
	 * Generates catchers positions.
	 *
	 * @return positions
	 *
	 * @author Tomasz Dąbrowski
	 */
	private List<Position> generateCatcherPositions() {
		List<Position> list = []
		switch (teamID()) {
			case TeamID.TEAM_A:
				list.add(new Position(10, 4))
				list.add(new Position(10, 11))
				list.add(new Position(7, 7))
				break
			case TeamID.TEAM_B:
				list.add(new Position(15, 4))
				list.add(new Position(15, 11))
				list.add(new Position(18, 7))
				break
		}
		list
	}

	/**
	 * Generates thrower positions.
	 *
	 * @return thrower positions
	 *
	 * @author Tomasz Dąbrowski
	 */
	private List<Position> generateThrowerPositions() {
		List<Position> list = []
		switch (teamID()) {
			case TeamID.TEAM_A:
				list.add(new Position(5, 4))
				list.add(new Position(5, 11))
				list.add(new Position(2, 7))
				break;
			case TeamID.TEAM_B:
				list.add(new Position(20, 4))
				list.add(new Position(20, 11))
				list.add(new Position(23, 7))
				break;
		}
		list
	}

	/**
	 * Gets start positions
	 *
	 * @return positions
	 *
	 * @author Tomasz Dąbrowski
	 */
	public List getStartPositions() {
		List<Position> catcherPositions = generateCatcherPositions()
		List<Position> throwerPositions = generateThrowerPositions()
		assert groups
		List startPs = groups.collect { PlayerGroup pg ->
			Position p
			switch(pg.mainPlayer.role()) {
				case Role.CATCHER:
					p = catcherPositions.remove(0)
					break
				case Role.THROWER:
					p = throwerPositions.remove(0)
					break
				default:
					p = (Math.random() > 0.5f) ? throwerPositions.remove(0) : catcherPositions.remove(0)
			}
			positionReqs.registerPosition(p)
			[pg.mainPlayer, p]
		}

		assert startPs.collect { it[1] }.size() == startPs.collect { it[1] }.unique().size()

		List startSupport = []
		groups.each { PlayerGroup pg ->
			pg.supportPlayers.each { Player p ->
				Position pos = getSupportPosition(pg.mainPlayer, startPs.find { it[0] == pg.mainPlayer }[1])
				positionReqs.registerPosition(pos)
				assert null == startSupport.find { it[1] == pos }
				assert null == startPs.find { it[1] == pos }
				startSupport.add([p, pos])
			}
		}

		assert startPs.every { it[0] && it[1] }
		assert (startPs + startSupport).collect { it[1] }.size() == (startPs + startSupport).collect { it[1] }.unique().size()


		def inScrimmage = {
			(startPs + startSupport).sum { Pitch.isScrimmageZone(it[1]) ? 1 : 0 }
		}

		List scrimmage = aiEngine.pitchRange().findAll { Position pos ->
			(aiEngine.team.id == TeamID.TEAM_A ? Pitch.isScrimmageZoneTeamA(pos) : Pitch.isScrimmageZoneTeamB(pos)) && !(startPs + startSupport).find { it[1] == pos}
		}.collect { it }
		assert scrimmage
		while(inScrimmage() < 3) {
			def movePPos = startSupport.findAll {
				!Pitch.isScrimmageZone(it[1])
			}.min { List pPos ->
				scrimmage.collect { Position pos ->
					pPos[1].distanceAlt(pos)
				}.min()
			}
			movePPos[1] = scrimmage.min { Position pos -> movePPos[1].distanceAlt(pos) }
			assert movePPos[1]
			scrimmage.remove(movePPos[1])
		}

		startPs.addAll(startSupport)
		//assert startPs.size() == Pitch.MAX_PLAYERS_ON_FIELD
		assert startPs.collect { it[1] }.size() == startPs.collect { it[1] }.unique().size()
		startPs
	}

	/**
	 * Gets support player position.
	 *
	 * @param mainPlayer main player
	 * @param mainPlayerPos main player destination
	 * @return position
	 *
	 * @author Tomasz Dąbrowski
	 */
	private Position getSupportPosition(Player mainPlayer, Position mainPlayerPos) {
		assert mainPlayerPos
		assert mainPlayer
		List ps
		int r = 0
		while(!ps) {
			ps = positionReqs.positions.findAll { Position pos ->
				pos.distance(mainPlayerPos) < (mainPlayer.MA() / 2 + r)
			}
			r++
		}
		Collections.shuffle(ps)
		ps[0]
	}

	/**
	 * Creates groups.
	 *
	 * @param players players
	 * @return groups
	 *
	 * @author Tomasz Dąbrowski
	 */
	private static List<PlayerGroup> createGroups(List<Player> players) {
		players = players.findAll {
			it.state() == PlayerState.IN_DUGOUT
		}
		List<Player> supportPlayers = players.findAll { Player p ->
			p.role() in [
				Role.BLOCKER,
				Role.LINEMAN,
				Role.BLITZER
			]
		}
		supportPlayers.sort { Player p -> p.role().value() }
		assert !supportPlayers.isEmpty()
		List<Player> mainPlayers = players - supportPlayers
		assert !mainPlayers.isEmpty()

		List ids = (mainPlayers.size()..1).collect { it }
		List<PlayerGroup> groups = mainPlayers.collect { Player p ->
			new PlayerGroup(mainPlayer : p, id : ids.pop())
		}
		supportPlayers = supportPlayers.subList(0, [
			Pitch.MAX_PLAYERS_ON_FIELD - groups.size(),
			supportPlayers.size()
		].min())

		supportPlayers.each { Player p ->
			groups.sort { PlayerGroup pg -> pg.supportPower }
			groups[0].addSupportPlayer(p)
		}

		groups.eachWithIndex { pg, pgi ->
			pg.mainPlayer.number = pgi * 20
			pg.supportPlayers.eachWithIndex { sp, spi ->
				sp.number = pgi * 20 + 5 + spi
			}
		}
		groups
	}

	/**
	 * Updates the leader group.
	 *
	 * @return forced leader group
	 *
	 * @author Tomasz Dąbrowski
	 */
	public PlayerGroup updateLeaderGroup(PlayerGroup playerGroup = null) {
		if(playerGroup != null) {
			leaderGroup = playerGroup
			return leaderGroup
		}
		Position ballPosition = aiEngine.ballPosition
		if(!ballPosition) {
			leaderGroup = null
			return
		}

		leaderGroup = groups.findAll { PlayerGroup pg ->
			pg.updateMainPlayer()
			pg?.mainPlayer.state() == PlayerState.IN_PLAY
		}.min { PlayerGroup pg ->
			pg.mainPlayer.hasBall() ? Integer.MIN_VALUE : pg.mainPlayer.distance(ballPosition)
		}
		leaderGroup
	}

	/**
	 * Gets all main players.
	 *
	 * @return main players
	 *
	 * @author Tomasz Dąbrowski
	 */
	public List<Player> getMainPlayers() {
		groups.collect { it.mainPlayer }
	}

	/**
	 * Gets all support players.
	 *
	 * @return support players
	 *
	 * @author Tomasz Dąbrowski
	 */
	public List<Player> getSupport() {
		groups.collect { it.supportPlayers }.flatten()
	}
}
